/*
 * Copyright 2007 Agile Plugins Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * 	http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.googlecode.agileplugins.checkstyle;

import com.intellij.codeInspection.InspectionManager;
import com.intellij.codeInspection.LocalQuickFix;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.codeInspection.ProblemHighlightType;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiWhiteSpace;
import com.puppycrawl.tools.checkstyle.api.AuditEvent;
import com.puppycrawl.tools.checkstyle.api.AuditListener;

import java.util.ArrayList;
import java.util.List;

class CheckStyleAuditListener implements AuditListener {
    private Logger logger = Logger.getInstance(getClass().getName());

    private List problems = new ArrayList();
    private InspectionManager inspectionManager;
    private PsiClass psiClass;
    private boolean firstClass;

    public CheckStyleAuditListener(InspectionManager inspectionManager, PsiClass psiClass) {
        this.inspectionManager = inspectionManager;
        this.psiClass = psiClass;
        firstClass = isFirstClass();
    }

    public void auditStarted(final AuditEvent auditEvent) {
    }

    public void auditFinished(AuditEvent auditEvent) {
    }

    public void fileStarted(final AuditEvent auditEvent) {
    }

    public void fileFinished(final AuditEvent auditEvent) {
    }

    public void addError(final AuditEvent auditEvent) {
        int line = auditEvent.getLine();
        int column = auditEvent.getColumn();
        String message = auditEvent.getMessage();
        String sourceName = auditEvent.getSourceName();
        if (!psiClass.isValid()) {
            logger.info("Class " + psiClass + " is invalid");
            return;
        }
        PsiFile containingFile = psiClass.getContainingFile();
        if (containingFile == null) {
            logger.info("Containing file for class " + psiClass + " is null.");
            return;
        }
        String text = containingFile.getText();
        int i = StringUtil.lineColToOffset(text, line - 1, column);
        PsiElement elementAt = containingFile.findElementAt(i);
        elementAt = findAppropriateElement(elementAt);
        PsiClass containingClass = getContainingClass(elementAt);
        if (psiClass == containingClass
                || (containingClass == null && firstClass)) {
            problems.add(createProblemDescriptor(
                    elementAt,
                    getErrorMessage(message, sourceName)));
        }
    }

    public PsiClass getContainingClass(PsiElement element) {
        while ((element != null && !(element instanceof PsiClass))) {
            element = element.getParent();
        }
        return (PsiClass) element;
    }

    public void addException(final AuditEvent auditEvent,
                             final Throwable throwable) {
        logger.info("throwable = " + throwable);
    }

    public ProblemDescriptor[] getProblemDescriptors() {
        return (ProblemDescriptor[]) problems.toArray(new ProblemDescriptor[problems.size()]);
    }

    private boolean isFirstClass() {
        PsiElement[] children = psiClass.getContainingFile().getChildren();
        for (int i = 0; i < children.length; i++) {
            PsiElement child = children[i];
            if (child == psiClass) {
                return true;
            }
            if (child instanceof PsiClass) {
                return false;
            }
        }
        return false;
    }

    private PsiElement findAppropriateElement(PsiElement elementAt1) {
        PsiElement elementAt = elementAt1;
        if (elementAt instanceof PsiWhiteSpace) {
            elementAt = elementAt.getNextSibling();
            if (elementAt != null) {
                elementAt = elementAt.getFirstChild();
            }
        }
        if (elementAt == null) {
            elementAt = psiClass.getFirstChild();
        }
        return elementAt;
    }

    private String getErrorMessage(final String message,
                                   final String sourceName) {
        String sourceName2 = sourceName.substring(sourceName.lastIndexOf(".") + 1);
        return "CheckStyle: " + message + "(" + sourceName2 + ")";
    }

    private ProblemDescriptor createProblemDescriptor(PsiElement psiElement, String message) {
        int textLength = psiElement.getTextLength();
        if (textLength == 0) {
            psiElement = psiElement.getNextSibling();
        }
        return inspectionManager.createProblemDescriptor(
                psiElement,
                message,
                (LocalQuickFix) null,
                ProblemHighlightType.GENERIC_ERROR_OR_WARNING);
    }
}
